/*
 * Copyright (c) 2005-2006 FeySoft Inc. All Rights Reserved.
 */
package com.feyaSoft.plugin.util;

import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

/**
 * @author fzhuang
 * @created May 9, 2005 1:40:35 PM This DesEncrypter.java is used to .... TODO
 *          To change the template for this generated type comment go to Window -
 *          Preferences - Java - Code Style - Code Templates
 *          
 *          This is copy from "http://javaalmanac.com/egs/javax.crypto/DesFile.html"
 */
public class DesEncrypter {
	Cipher ecipher;
	Cipher dcipher;

	// 8-byte Salt
	byte[] salt = { (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
			(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };

	// Iteration count
	int iterationCount = 19;

	public DesEncrypter(String passPhrase) {
		try {
			// Create the key
			KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt,
					iterationCount);
			SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
					.generateSecret(keySpec);
			ecipher = Cipher.getInstance(key.getAlgorithm());
			dcipher = Cipher.getInstance(key.getAlgorithm());

			// Prepare the parameter to the ciphers
			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
					iterationCount);

			// Create the ciphers
			ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
		} catch (Exception e) {
		}
	}

	/**
	 * another way to choose public and private key, and field key...
	 * 
	 * @param passPhrase
	 * @param userId
	 */
	public DesEncrypter(String passPhrase, Long userId, String fieldKey) {
		try {
			// Create the key
			String pubPriKey = passPhrase + fieldKey + userId.toString();

			KeySpec keySpec = new PBEKeySpec(pubPriKey.toCharArray(), salt,
					iterationCount);
			SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
					.generateSecret(keySpec);
			ecipher = Cipher.getInstance(key.getAlgorithm());
			dcipher = Cipher.getInstance(key.getAlgorithm());

			// Prepare the parameter to the ciphers
			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
					iterationCount);

			// Create the ciphers
			ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
		} catch (Exception e) {
		}
	}
	
	/**
	 * another way to choose public and private key, and field key...
	 * 
	 * @param passPhrase
	 * @param userId
	 */
	public DesEncrypter(String passPhrase, String fieldKey) {
		Long userId = new Long(98765);
		try {
			// Create the key
			String pubPriKey = passPhrase + fieldKey + userId.toString();

			KeySpec keySpec = new PBEKeySpec(pubPriKey.toCharArray(), salt,
					iterationCount);
			SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
					.generateSecret(keySpec);
			ecipher = Cipher.getInstance(key.getAlgorithm());
			dcipher = Cipher.getInstance(key.getAlgorithm());

			// Prepare the parameter to the ciphers
			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
					iterationCount);

			// Create the ciphers
			ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
		} catch (Exception e) {
		}
	}

	/**
	 * Just encrypt the input file and get the result back
	 * to customer
	 * 
	 * @param str
	 * @return
	 */
	public String encrypt(String str) {
		try {
			// Encode the string into bytes using utf-8
			byte[] utf8 = str.getBytes("UTF8");

			// Encrypt
			byte[] enc = ecipher.doFinal(utf8);

			// Encode bytes to base64 to get a string
			String result = new sun.misc.BASE64Encoder().encode(enc);

			// Base 64 armouring uses only the characters A-Z, a-z, 0-9 and +/=.
			// This makes it suitable for encoding binary data as SQL strings,
			// that will work no matter what the encoding.
			// Unfortunately + / and = all have special meaning in URLs.
			// And we need pass this result in URL.
			// In here, we replace "+" with "$". During decrypt, we change back.
			result = result.replace('+', '$');

			return result;
		} catch (Exception e) {
		}

		return null;
	}

	/**
	 * we know the encrypt the result is 12/24/32 character.
	 * Now we decrypt it and return back.
	 * 
	 * @param str
	 * @return
	 */
	public String decrypt(String str) {
		try {
			// first step, change back $ -> + if any
			// because we define in the encrypt.
			String encryptString = str.replace('$', '+');

			// Decode base64 to get bytes
			byte[] dec = new sun.misc.BASE64Decoder()
					.decodeBuffer(encryptString);

			// Decrypt
			byte[] utf8 = dcipher.doFinal(dec);

			// Decode using utf-8
			String decryptResult = new String(utf8, "UTF8");
			return decryptResult;

		} catch (Exception e) {
		}

		return null;
	}
	
	/**
	 * push the orig number and encrypt as together and give it to the user...
	 * 
	 * @param str
	 * @return
	 */
	public String encryptAppend(String str) {
		try {
			// Encode the string into bytes using utf-8
			byte[] utf8 = str.getBytes("UTF8");

			// Encrypt
			byte[] enc = ecipher.doFinal(utf8);

			// Encode bytes to base64 to get a string
			String result = new sun.misc.BASE64Encoder().encode(enc);

			// Base 64 armouring uses only the characters A-Z, a-z, 0-9 and +/=.
			// This makes it suitable for encoding binary data as SQL strings,
			// that will work no matter what the encoding.
			// Unfortunately + / and = all have special meaning in URLs.
			// And we need pass this result in URL.
			// In here, we replace "+" with "$". During decrypt, we change back.
			result = result.replace('+', '$');

			return result + str;
		} catch (Exception e) {
		}

		return null;
	}

	/**
	 * we know the encrypt the result is 12 character, so I get the first 12 as
	 * encrypt result. And the other part is the orig number. If compare the
	 * decrypt result with the orig number. If not equal, failed.
	 * 
	 * @param str
	 * @return
	 */
	public String decryptAppend(String str) {
		try {
			// first step, change back $ -> + if any
			// because we define in the encrypt.
			String encryptString = str.substring(0, 12);
			String origString = str.substring(12, str.length());
			encryptString = encryptString.replace('$', '+');

			// Decode base64 to get bytes
			byte[] dec = new sun.misc.BASE64Decoder()
					.decodeBuffer(encryptString);

			// Decrypt
			byte[] utf8 = dcipher.doFinal(dec);

			// Decode using utf-8
			String decryptResult = new String(utf8, "UTF8");

			if (decryptResult.equals(origString)) {
				return origString;
			}

		} catch (Exception e) {
		}

		return null;
	}
}